#!/usr/bin/python

import commands
import glob
import os
import re
import sys
import time
import unittest


DEFAULT_PROGRAM_PATH = "/usr/bin/python ./short_wrapper.py"
PROGRAM_PATH = os.environ.get("SHORT_PROGRAM", DEFAULT_PROGRAM_PATH)
SPECIFIC_TEST = os.environ.get("SHORT_SPECIFIC_TEST", "")
TESTS_DIR = os.environ.get("SHORT_TESTS_DIR", "random")
TEMP_DIR = os.environ.get("SHORT_TMP_DIR", "tmp")
TESTS_INPUT_SUFFIX = ".in.txt"
TESTS_OUTPUT_SUFFIX = ".out.txt"
TESTS_COMMANDS_SUFFIX = ".real.in.txt"

def str_to_int(s):
    num = ""
    for c in s:
        if c.isdigit():
            num += c
        else:
            break
    return int(num)

def run_program(name):
    """
    Runs the given test name by locating the correct test file input and output
    and then compares the output - returning both the real and expected ouput
    and error.
    """
    # make sure the temp dir exists
    try:
        os.makedirs(TEMP_DIR)
    except:
        pass
    
    # set up all file names for run
    test_in_file = TESTS_DIR + os.sep + name + TESTS_INPUT_SUFFIX
    test_out_file = TEMP_DIR + os.sep + name + TESTS_OUTPUT_SUFFIX
    test_command_file = TEMP_DIR + os.sep + name + TESTS_COMMANDS_SUFFIX
    expected_out_file = TESTS_DIR + os.sep + name + TESTS_OUTPUT_SUFFIX
    test_command = "%s %s %s %s" % (
        PROGRAM_PATH, test_in_file, test_out_file, test_command_file)
    
    # run the actual program
    os.system(test_command)
    
    # compare the output
    try:
      real_out = file(test_out_file, "r").read()
    except:
      raise RuntimeError(
          ("Output file from your code ('%s') does not exist!\n" +
           "Possible reasons:\n" +
           "1) SEGMENTATION FAULT in your code prevented output\n" +
           "2) No more free space on hard drive (especially on T2)\n" +
           "   You could try deleting tests/random and tests/tmp directories") %
          (test_out_file))
    expected_out = file(expected_out_file, "r").read()
    
    return (test_out_file, real_out, expected_out_file, expected_out)


class TestProgramRun(unittest.TestCase):
    """
    This is the base test case which performs a single run of the program, and
    compares it's output to the expected one.
    """
    
    def __init__(self, name):
        self.name = name
        unittest.TestCase.__init__(self, "testRun")
        
    def id(self):
        return "TestProgramRun.testRun(" + self.name + ")"

    def replaceNumberRanges(self, real_line, expected_line):
        real_words = real_line.split()
        expected_words = expected_line.split()
        for i in xrange(len(expected_words)):
            if re.match("^[0-9]+-[0-9]+$", expected_words[i]):
                limits = [int(r) for r in expected_words[i].split("-")]
                try:
                    if limits[0] <= int(real_words[i]) <= limits[1]:
                        real_words[i] = expected_words[i]
                except:
                    pass
        return " ".join(real_words), " ".join(expected_words)
    
    def testRun(self):
        print "(%s) ... " % self.name,
        sys.stdout.flush()
        time.sleep(0.5)
        real_out_file, real_out, expected_out_file, expected_out = \
            run_program(self.name)
        real_out = real_out.split("\n")
        expected_out = expected_out.split("\n")
        for i in range(0, len(expected_out)):
            real_line = real_out[i]
            expected_line = expected_out[i]
            real_line, expected_line = self.replaceNumberRanges(real_line, expected_line)
            self.assertEquals(
                " ".join(real_line.split()),
                " ".join(expected_line.split()),
                self.name + (": line %d differs in output:\n" +
                    "real_out (file:'%s')\t: '%s'\n!=\n" +
                    "expected (file:'%s')\t: '%s'") % (
                    (i+1), real_out_file, real_line,
                    expected_out_file, expected_line))
        self.assertEquals(len(expected_out), len(real_out),
            self.name + ": length of expected output and actual output differ")


# create all of the actual test suites
if SPECIFIC_TEST:
	  test_files = []
	  test_patterns = SPECIFIC_TEST.split(",")
	  for pattern in test_patterns:
		    test_files += glob.glob("%s%s%s%s" % (
			      TESTS_DIR, os.sep, pattern, TESTS_INPUT_SUFFIX))
else:
	  test_files = glob.glob(TESTS_DIR + os.sep + "*.in.txt")
test_files = [f[len(TESTS_DIR + os.sep):f.rindex(TESTS_INPUT_SUFFIX)] for f in test_files]
test_cases = [TestProgramRun(f) for f in test_files]

# create the suite and run them
suite = unittest.TestSuite()
suite.addTests(test_cases)
if __name__ == "__main__":
    unittest.TextTestRunner(verbosity=2).run(suite)
